[Rust] Amazon KendraでGetSnapshots APIを使う
Introduction
Amazon Kendraの GetSnapshots APIは、Kendraの検索分析データを取得するためのAPIです。
このAPIを使うことで、Kendraに対して行われた検索クエリの統計情報を取得できます。
例えば、特定の期間にどれだけの検索が行われたか、どのようなクエリで検索されたか、
検索結果の有用性などの情報を取得できます。
これらの分析結果から、Kendraの改善に役立てることができます。
Environment
- MacBook Pro (14-inch, M3, 2023)
- OS : MacOS 14.5
- Rust : 1.81.0
AWSアカウントはすでにあり、
アクセス可能なKendraも存在するとします。
KendraのIndexはこのあたりをみて作成しましょう。
Getsnapshots API
では実際に試してみましょう。
期間とMetricTypeを指定してDKで用意してあるAPIを実行するだけです。
ここの内容にそって進めていきます。
APIで指定するパラメータは下記です。
重要なパラメータについていくつか解説します。
{
"IndexId": "<Kendra Index ID>",
"Interval": "期間",
"MaxResults": <データ取得数>,
"MetricType": "メトリクスタイプ",
"NextToken": "<任意>"
}
期間(Interval)
指定する期間は、いつのデータを統計対象にするかを指定します。
以下は、現在の日付を「2024年9月17日(火曜日)」とした場合の例です。
期間 | 説明 | データ取得期間(2024年9月17日基準) |
---|---|---|
THIS_WEEK |
日曜日から始まり、現在の日付の前日に終わる現在の週。 | 2024年9月15日(日)~2024年9月16日(月) |
ONE_WEEK_AGO |
日曜日に始まり、次の土曜日に終わる前の週。 | 2024年9月8日(日)~2024年9月14日(土) |
TWO_WEEKS_AGO |
前の週の前の週。日曜日に始まり、次の土曜日に終わる。 | 2024年9月1日(日)~2024年9月7日(土) |
THIS_MONTH |
現在の月。その月の1日から始まり、現在の日付の前日に終わる。 | 2024年9月1日(日)~2024年9月16日(月) |
ONE_MONTH_AGO |
前月。月の最初の日から始まり、月の最後の日に終わる。 | 2024年8月1日(木)~2024年8月31日(土) |
TWO_MONTHS_AGO |
前月の前月。その月の1日から始まり、その月の末日まで続く。 | 2024年7月1日(月)~2024年7月31日(水) |
このように、GetSnapshot
APIを使用する際に、指定した期間に基づいてデータを取得することができます。
期間の指定は、↓にあるようにAPI実行タイミングによって結果に違いが出るので注意しましょう。
タイミング | 説明 |
---|---|
週の変わり目(日曜日)に実行 | - THIS_WEEK のデータが空になる可能性あり。- ONE_WEEK_AGO とTWO_WEEKS_AGO のデータが1週間ずつシフトする。 |
月の変わり目に実行 | - THIS_MONTH のデータが空になる可能性あり。- ONE_MONTH_AGO とTWO_MONTHS_AGO のデータが1ヶ月ずつシフトする。 |
日付が変わる直前や直後に実行 | - THIS_WEEK とTHIS_MONTH のデータ量が、実行時刻によって大きく異なる可能性あり |
月末の日数が異なる月をまたぐ場合 | - ONE_MONTH_AGO とTWO_MONTHS_AGO のデータ量が月によって異なる可能性あり |
MetricType
もう1つのパラメータが、取得するデータのタイプです。
タイプは↓のように6つあり、「検索されたクエリ」だったり
「検索後にユーザーがクリックしなかったクエリ」などです。
メトリクスタイプ | 説明 |
---|---|
QUERIES_BY_COUNT |
検索結果の有無に関わらず、実行されたすべての検索クエリの数 |
QUERIES_BY_ZERO_CLICK_RATE |
検索結果が表示されたが、ユーザーがクリックしなかったクエリの割合 |
QUERIES_BY_ZERO_RESULT_RATE |
検索結果が0件だったクエリの割合 |
DOCS_BY_CLICK_COUNT |
検索結果として表示され、ユーザーにクリックされたドキュメントの数 |
AGG_QUERY_DOC_METRICS |
検索クエリと関連するドキュメントに関する集計情報 |
TREND_QUERY_DOC_METRICS |
検索クエリと関連するドキュメントに関するトレンド情報 |
こういったパラメータを指定することで、下記のようにCLIで簡単にデータを取得できます。
% aws kendra get-snapshots \
--index-id <Knedra Index ID> \
--interval "ONE_WEEK_AGO" \
--metric-type "QUERIES_BY_COUNT"
//response
{ "SnapShotTimeFilter": { "StartTime": "2024-xx-xxT00:00:00+09:00",
"EndTime": "2024-xx-xxT23:00:00+09:00" },
"SnapshotsDataHeader": [ "query_content",
"count", "ctr", "zero_click_rate", "click_depth",
"instant_answer", "confidence" ],
"SnapshotsData": [
[ "hoge", "23", "0.0", "1.0", "0.0", "0.0", "LOW" ],
・・・
],
"NextToken": "xxxxx" }
SnapshotsDataHeaderは、各列のヘッダーを表しており、
内容は下記です。
- query_content: 検索クエリ内容
- count: クエリ実行回数
- ctr: スルーしたクリックの割合
- zero_click_rate: 結果を見たが何もクリックしなかった割合
- click_depth: 何番目の結果をクリックしたか
- instant_answer: 即時回答の割合
- confidence: 信頼度
SnapshotsDataの各行は検索クエリに関するデータです。
例えば、最初の行の意味は下記のようになります。
- "hoge" が23回検索された
- クリックスルー率は0%
- ゼロクリック率は100%(すべての検索で結果をクリックしていない)
- クリック深度は0(クリックがないため)
- インスタントアンサーの割合は0%
- 信頼度は低い(LOW)
Try
では、Getsnapshot APIをRust用SDKで試してみます。
ここではQUERIES_BY_COUNTとQUERIES_BY_ZERO_RESULT_RATEをメトリクスを取得してみます。
Cargoでプロジェクトを作成して必要なcrateをaddします。
% cargo new kendra-edample
% cd kendra-edample
% cargo add anyhow aws-config aws-sdk-kendra tokio thiserror
src/main.rsを下記のように記述します。
use aws_sdk_kendra::{
types::{Interval, MetricType},
config::BehaviorVersion,
Client,
};
use anyhow::{Context, Result};
const INDEX_ID: &str = "Kendta Index ID";
const MAX_RESULTS: i32 = 100;
#[tokio::main]
async fn main() -> Result<()> {
let config = aws_config::load_defaults(BehaviorVersion::v2024_03_28()).await;
let client = Client::new(&config);
let interval = Interval::OneWeekAgo;
// QUERIES_BY_COUNT データの取得
let count_data = get_snapshot_data(&client, interval.clone(), MetricType::QueriesByCount)
.await
.context("Failed to get QUERIES_BY_COUNT data")?;
let sorted_count_queries = sort_by_count(&count_data);
// QUERIES_BY_ZERO_RESULT_RATE データの取得
let zero_rate_data = get_snapshot_data(&client, interval, MetricType::QueriesByZeroResultRate)
.await
.context("Failed to get QUERIES_BY_ZERO_RESULT_RATE data")?;
let sorted_zero_rate_queries = sort_by_zero_result_rate(&zero_rate_data);
// 人気のあるクエリを出力
println!("\n人気のあるクエリ (QUERIES_BY_COUNT):");
for (query, count) in &sorted_count_queries {
println!("クエリ: {}, 検索回数: {}", query, count);
}
println!("Total: {}", sorted_count_queries.len());
// ゼロ結果率の高いクエリを出力
println!("\nゼロ結果率の高いクエリ (QUERIES_BY_ZERO_RESULT_RATE):");
for (query, zero_rate) in &sorted_zero_rate_queries {
println!("クエリ: {}, ゼロ結果率: {}", query, zero_rate);
}
println!("Total: {}", sorted_zero_rate_queries.len());
Ok(())
}
async fn get_snapshot_data(
client: &Client,
interval: Interval,
metric_type: MetricType,
) -> Result<Vec<Vec<String>>> {
let mut all_data = Vec::new();
let mut next_token: Option<String> = None;
let mut request_count = 0;
loop {
request_count += 1;
let response = client
.get_snapshots()
.index_id(INDEX_ID)
.interval(interval.clone())
.metric_type(metric_type.clone())
.max_results(MAX_RESULTS)
.set_next_token(next_token.clone())
.send()
.await
.context("Failed to send get_snapshots request")?;
let items_received = response.snapshots_data().len();
println!(
"Request {}: Received {} items.",
request_count, items_received
);
all_data.extend(response.snapshots_data().iter().cloned());
next_token = response.next_token().map(|s| s.to_string());
if next_token.is_none() {
break;
}
}
println!("Total requests made: {}", request_count);
println!("Total items fetched: {}", all_data.len());
Ok(all_data)
}
fn sort_by_count(data: &Vec<Vec<String>>) -> Vec<(String, i32)> {
let mut queries_with_count: Vec<(String, i32)> = data
.iter()
.map(|item| {
let query = item[0].clone();
let count = item[1].parse::<i32>().unwrap_or(0); // 検索回数はitem[1]
(query, count)
})
.collect();
// 検索回数で降順にソート
queries_with_count.sort_by(|a, b| b.1.cmp(&a.1));
queries_with_count
}
fn sort_by_zero_result_rate(data: &Vec<Vec<String>>) -> Vec<(String, f64)> {
let mut queries_with_zero_rate: Vec<(String, f64)> = data
.iter()
.map(|item| {
let query = item[0].clone();
let zero_rate = item[2].parse::<f64>().unwrap_or(0.0); // ゼロ結果率はitem[2]
(query, zero_rate)
})
.collect();
// ゼロ結果率で降順にソート
queries_with_zero_rate.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
queries_with_zero_rate
}
このプログラムはKendraのGetSnapshots
APIを使用して検索クエリの統計情報を取得します。
QUERIES_BY_COUNT
メトリクスでは、検索回数が多いクエリを取得し、
QUERIES_BY_ZERO_RESULT_RATE
メトリクスでは、
検索結果が0件だったクエリの割合を取得します。
get_snapshot_data
関数で、指定したintervalとmetrics typeに基づいてデータを取得します。
データは複数ページにわたる可能性があるため、next_token
を使用してpagenationも考慮。
その後取得したデータをsort_by_count
/sort_by_zero_result_rate
でソートします。
私の環境で実行した結果、↓のようになりました。
人気のあるクエリ (QUERIES_BY_COUNT):
クエリ: The Shawshank Redemption, 検索回数: 1032
クエリ: I'm going to make him an offer he can't refuse., 検索回数: 202
クエリ: Pride and Prejudice, 検索回数: 124
クエリ: Inception, 検索回数: 120
クエリ: One Hundred Years of Solitude, 検索回数: 118
クエリ: Jurassic Park, 検索回数: 56
クエリ: The Godfather, 検索回数: 35
クエリ: I'll be back., 検索回数: 21
・
・
・
Total: xxxx
ゼロ結果率の高いクエリ (QUERIES_BY_ZERO_RESULT_RATE):
クエリ: hoge, ゼロ結果率: 1
クエリ: fugafuga, ゼロ結果率: 1
クエリ: test, ゼロ結果率: 1
クエリ: jjj, ゼロ結果率: 0.5
・
・
・
Total: xxxx
Summary
今回はAmazon KendraのGetSnapshots APIをRustで使用し、検索クエリの統計データを取得してみました。
各種メトリクスを指定し、検索回数やゼロ結果率の高いクエリを分析します。
こういったデータを定期的に分析することで、
Kendraのクエリパフォーマンスを評価し、改善に役立てることができます。